package com.sunnyatwal.kettlebell.db;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import android.content.Context;
import android.util.Log;

import com.db4o.Db4oEmbedded;
import com.db4o.ObjectContainer;
import com.db4o.ObjectSet;

public abstract class Database<E, K> {

	private static final String TAG = "DATABASE";
	
	private Class<E> mType;
	private boolean mIsDbOpen = false;
	private ObjectContainer mSource = null;
	private String mPath = "";
	
	
	public static String buildPath(Context c, String dir, String file) {
		return c.getDir(dir, 0) + "/" + file;
	}
	
	
	public Database(Class<E> type, String path) {
		mType = type;
		mPath = path;
	}
	
	
	public ObjectContainer open() throws IOException {
		
		try {
			
			if (!mIsDbOpen) {
				mSource = Db4oEmbedded.openFile(mPath);
				Log.i(TAG, "opened new database connection");
				mIsDbOpen = true;
			}
 
			return mSource;
			
		} 
		catch (Exception e) {
			Log.i(TAG, "unable to open database");
			throw new IOException();
		}
	}
	
	
	public void close() {
		
		if (mIsDbOpen) {
			mSource.close();
			Log.i(TAG, "closed database connection");
			mIsDbOpen = false;
		}
	};
	
	
	public boolean add(E entry) {
		
		if (mIsDbOpen) {
			mSource.store(entry);
			mSource.commit();
			return true;
		}
		else {
			Log.i(TAG, "can't add to a closed database");
		}
		
		return false;
	};
	
		
	public boolean delete(E entry) {
		
		E found = null;
		
		try {
			
			ObjectSet<E> result = mSource.queryByExample(entry);		
	        if (result.hasNext()) {
	            found = result.next();
	            mSource.delete(found);
	            return true;
	        } 
	        else {
	        	
	        	Log.i(TAG, "Entry not found!");
	            return false;
	        }
		} 
		catch(Exception e2) {
			
			Log.i(TAG, e2.toString());
			return false;
		}
	};
	
	
	public boolean deleteSorted(int i) {		
		return delete(getSortedList().get(i));
	}
	
	
	public boolean isEmpty() {
		
		try {
			
			ObjectSet<E> result = mSource.queryByExample(mType.newInstance());		
	        if (result.hasNext()) {
	            return true;
	        }
	        else {
	            return false;
	        }
	        
		} 
		catch(Exception e2) {
			Log.i(TAG, e2.toString());
			return false;
		}
	};
	
	
	public boolean update(E old, E update) {
		
		if (delete(old)) {
			
			add(update);
			return true;
		}
		else {
			return false;
		}
	}
	
	
	public boolean update(int i, E update) {		
		return update(getSortedEntry(i), update);
	}
	
	
	public List<E> getList() {
		
		List<E> list = new ArrayList<E>();
		
		try {
			
			ObjectSet<E> result = mSource.queryByExample(mType.newInstance());
			
			while (result.hasNext()) {
				E found = result.next();
				list.add(found);			
			}
			
		} 
		catch(Exception e) {
			Log.i(TAG, e.toString());
		}
		 		
		return list;		
	};
	
	
	public List<E> getSortedList() {
		
		List<E> list = getList();
		
		class CustomComparator implements Comparator<E> {
			@Override
			public int compare(E lhs, E rhs) {				
				return compareEntries(lhs, rhs);
			}
    	}
		
		Collections.sort(list, new CustomComparator());
    	
    	return list;
	}
	
	
	public E getSortedEntry(int i) {		
		return getSortedList().get(i);		
	}
	
	
	public int getSortedEntry(K key) {
		
		List<E> list = getList();
		
		for(int i = 0; i < list.size(); i++) {
			if (isEqual(list.get(i), key)) {
				return i;
			}
		}
		
		return -1;
	}
	
	
	public boolean doesEntryExist(K key) {
		
		List<E> list = getList();
		
		for(int i = 0; i < list.size(); i++)
			if (isEqual(list.get(i), key))
				return true;
		
		return false;
	}
	
	
	public abstract int compareEntries(E lhs, E rhs);
	
	public abstract boolean isEqual(E firstEntry, K key);
	
	
}
